2025年12月19日
RAG
RAG構築: OpenAIとOllamaでの構築の違いと注意点
RAG(Retrieval-Augmented Generation)システムを構築する際、Embeddingモデルの選択は、システムの性能、コスト、運用体制に大きな影響を与える重要な意思決定です。本記事では、OpenAI APIとOllama(ローカルLLM)それぞれでRAGを構築する際の実装上の違いと、実際のプロダクション環境で直面する課題について解説します。

Embeddingモデルの選択
OpenAI API
OpenAIではtext-embedding-3-smallが現在の推奨モデルです。主な特徴は以下の通りです:
- 次元数: 1536次元(
dimensionsパラメータで256~1536の範囲で調整可能) - コスト: )
- 性能: MTEB(Massive Text Embedding Benchmark)で高いスコアを記録
- コンテキスト長: 8,191トークンまで対応
次元数を削減することで、ベクトルDBのストレージコストを抑えつつ、検索性能の大幅な低下を避けることができます。例えば、512次元に削減しても、多くのユースケースで実用的な精度を維持できます。
Ollama(ローカルLLM)
Ollamaでは以下のオープンソースモデルが利用可能です:
- nomic-embed-text: 768次元、コンテキスト長8,192トークン
- mxbai-embed-large: 1024次元、高精度だがリソース消費大
- all-minilm: 384次元、軽量で高速
ローカル実行のため、API利用料が発生せず、データの外部送信が不要という利点があります。医療・金融などの機密性の高いデータを扱う場合、特に有効です。
バッチ処理の実装上の違い
最も注意すべき違いは、Embedding生成時のバッチ処理の仕組みです。
OpenAI APIの実装
OpenAIはバッチAPIをサポートしており、複数テキストを1回のAPI呼び出しで処理できます:
from openai import OpenAI client = OpenAI() # 100チャンクを一度に処理 response = client.embeddings.create( model="text-embedding-3-small", input=chunks, # List[str]で最大2048要素まで dimensions=512 # オプション ) embeddings = [data.embedding for data in response.data ]
この実装により:
- レイテンシの削減: 100チャンクでも1回のリクエストで済む
- レート制限の回避: リクエスト数ベースの制限(3,000 RPM)を効率的に使用
- コスト最適化: バッチ処理により通信オーバーヘッドを削減
実測では、100チャンクの処理で約2-3秒程度です(ネットワーク環境に依存)。
Ollamaの実装
Ollamaは現在、バッチAPIをサポートしていないため、各テキストを順次処理する必要があります:
import ollama embeddings = [] for chunk in chunks: response = ollama.embeddings( model='nomic-embed-text', prompt=chunk ) embeddings.append(response['embedding'])
この実装の特性:
- ローカル処理: ネットワーク遅延なし、データは外部送信されない
- リソース依存: CPU/GPUの性能に大きく依存(GPU使用時は大幅に高速化)
- 並列化可能: 非同期処理やマルチスレッドで高速化できる
実測では、M1 Mac(GPU使用)で1チャンクあたり約50-100msです。100チャンクなら5-10秒程度となります。
並列化による最適化
Ollamaの場合、以下のように並列処理を実装することで高速化できます:
import asyncio import ollama async def get_embedding(chunk): return await ollama.embeddings( model='nomic-embed-text', prompt=chunk ) # 並列処理(同時実行数を制限) embeddings = await asyncio.gather(*[ get_embedding(chunk) for chunk in chunks ])
ベクトルDBへの格納とメタデータ設計
どちらのアプローチでも、効率的なデータ管理のためにメタデータ設計が重要です。
推奨メタデータ構造
metadata = { "document_id": "doc_12345", # 必須 "chunk_index": 0, "source": "product_manual.pdf", "created_at": "2024-01-15T10:30:00Z", "page_number": 5, "section": "第3章" }
document_idを含めることで:
- 差分更新: 特定ドキュメントのチャンクのみを再生成・更新可能
- 削除操作: ドキュメント単位での一括削除が容易
- デバッグ: 検索結果の追跡とトラブルシューティングが簡単
実装例(ChromaDB)
import chromadb client = chromadb.Client() collection = client.create_collection("documents") # ドキュメント更新時は既存チャンクを削除 collection.delete( where={"document_id": "doc_12345"} ) # 新しいチャンクを追加 collection.add( embeddings=embeddings, documents=chunks, metadatas=metadatas, ids=[f"doc_12345_chunk_{i}" for i in range(len(chunks))] )
コストとパフォーマンスの比較
OpenAI API
メリット:
- 高精度な検索結果
- インフラ管理不要
- スケーラビリティが高い
コスト例:
- 10万ページのドキュメント: 約$32
- 月間100万クエリ(平均200トークン/クエリ): 約$40
- 合計: 初期費用40程度
デメリット:
- 従量課金コスト
- データを外部送信
- APIレート制限
Ollama
メリット:
- API利用料なし(サーバーコストのみ)
- データの外部送信不要
- レート制限なし
コスト例:
- GPU搭載サーバー(AWS g4dn.xlarge): 約$150/月
- ストレージ: 約$10/月
- 合計: 月額$160程度(固定費)
デメリット:
- インフラ運用が必要
- 精度がOpenAIより劣る可能性
- スケールアップにハードウェア投資が必要
選択基準とユースケース
OpenAI APIが適しているケース
- スタートアップ・小規模運用: 初期投資を抑えたい
- 可変ワークロード: 月ごとの利用量が大きく変動する
- 最高精度が必要: カスタマーサポートなど精度が重要
- 開発リソース不足: インフラ管理に人員を割けない
Ollamaが適しているケース
- 大規模・安定運用: 月間数百万クエリ以上で従量課金が高コスト
- データ主権重視: 医療・金融など機密データを扱う
- オフライン環境: インターネット接続が制限される環境
- カスタマイズ重視: モデルのファインチューニングを行いたい
ハイブリッドアプローチ
実務では、両方を組み合わせる戦略も有効です:
- 開発環境: OpenAI APIで迅速にプロトタイプ構築
- 本番環境: Ollamaで運用コスト削減
- フォールバック: Ollama障害時にOpenAI APIへ切り替え
まとめ
RAGシステムでのEmbedding選択は、単なる技術選定ではなく、ビジネス要件とのバランスを取る重要な意思決定です。OpenAI APIは開発速度と精度を優先する場合に、Ollamaはコスト管理とデータ主権を重視する場合に適しています。
プロジェクトの成熟度に応じて段階的に移行する柔軟なアプローチも検討する価値があります。重要なのは、最初から完璧を目指すのではなく、実装・測定・改善のサイクルを回すことです。